home *** CD-ROM | disk | FTP | other *** search
/ Turnbull China Bikeride / Turnbull China Bikeride - Disc 2.iso / STUTTGART / UNIXTOOL / M4SRC / _files / _M4 / Main._C < prev    next >
Encoding:
Text File  |  1989-10-15  |  12.5 KB  |  559 lines

  1. /*
  2.  * main.c
  3.  * Facility: m4 macro processor
  4.  * by: oz
  5.  */
  6.  
  7. #include "mdef.h"
  8. #include "extr.h"
  9.  
  10. #include <kernel.h>
  11. #include <signal.h>
  12. #include <string.h>
  13. #include <stdlib.h>
  14. #include <sys/misc.h>
  15.  
  16. /*
  17.  * m4 - macro processor
  18.  *
  19.  * PD m4 is based on the macro tool distributed with the software 
  20.  * tools (VOS) package, and described in the "SOFTWARE TOOLS" and 
  21.  * "SOFTWARE TOOLS IN PASCAL" books. It has been expanded to include 
  22.  * most of the command set of SysV m4, the standard UN*X macro processor.
  23.  *
  24.  * Since both PD m4 and UN*X m4 are based on SOFTWARE TOOLS macro,
  25.  * there may be certain implementation similarities between
  26.  * the two. The PD m4 was produced without ANY references to m4
  27.  * sources.
  28.  *
  29.  * References:
  30.  *
  31.  *    Software Tools distribution: macro
  32.  *
  33.  *    Kernighan, Brian W. and P. J. Plauger, SOFTWARE
  34.  *    TOOLS IN PASCAL, Addison-Wesley, Mass. 1981
  35.  *
  36.  *    Kernighan, Brian W. and P. J. Plauger, SOFTWARE
  37.  *    TOOLS, Addison-Wesley, Mass. 1976
  38.  *
  39.  *    Kernighan, Brian W. and Dennis M. Ritchie,
  40.  *    THE M4 MACRO PROCESSOR, Unix Programmer's Manual,
  41.  *    Seventh Edition, Vol. 2, Bell Telephone Labs, 1979
  42.  *
  43.  *    System V man page for M4
  44.  *
  45.  * Modification History:
  46.  *
  47.  * Jan 28 1986 Oz    Break the whole thing into little
  48.  *            pieces, for easier (?) maintenance.
  49.  *
  50.  * Dec 12 1985 Oz    Optimize the code, try to squeeze
  51.  *            few microseconds out..
  52.  *
  53.  * Dec 05 1985 Oz    Add getopt interface, define (-D),
  54.  *            undefine (-U) options.
  55.  *
  56.  * Oct 21 1985 Oz    Clean up various bugs, add comment handling.
  57.  *
  58.  * June 7 1985 Oz    Add some of SysV m4 stuff (m4wrap, pushdef,
  59.  *            popdef, decr, shift etc.).
  60.  *
  61.  * June 5 1985 Oz    Initial cut.
  62.  *
  63.  * Implementation Notes:
  64.  *
  65.  * [1]    PD m4 uses a different (and simpler) stack mechanism than the one 
  66.  *    described in Software Tools and Software Tools in Pascal books. 
  67.  *    The triple stack nonsense is replaced with a single stack containing 
  68.  *    the call frames and the arguments. Each frame is back-linked to a 
  69.  *     previous stack frame, which enables us to rewind the stack after 
  70.  *     each nested call is completed. Each argument is a character pointer 
  71.  *    to the beginning of the argument string within the string space.
  72.  *    The only exceptions to this are (*) arg 0 and arg 1, which are
  73.  *     the macro definition and macro name strings, stored dynamically
  74.  *    for the hash table.
  75.  *
  76.  *        .                       .
  77.  *    |   .    |  <-- sp            |  .  |
  78.  *    +-------+                +-----+
  79.  *    | arg 3 ------------------------------->| str |
  80.  *    +-------+                |  .  |
  81.  *    | arg 2 --------------+            .
  82.  *    +-------+          |
  83.  *        *              |            |     |
  84.  *    +-------+          |         +-----+
  85.  *    | plev    |  <-- fp     +---------------->| str |
  86.  *    +-------+                |  .  |
  87.  *    | type    |                   .
  88.  *    +-------+
  89.  *    | prcf    -----------+        plev: paren level
  90.  *    +-------+         |        type: call type
  91.  *    |   .    |        |        prcf: prev. call frame
  92.  *        .              |
  93.  *    +-------+       |
  94.  *    |    <----------+
  95.  *    +-------+
  96.  *
  97.  * [2]    We have three types of null values:
  98.  *
  99.  *        nil  - nodeblock pointer type 0
  100.  *        null - null string ("")
  101.  *        NULL - Stdio-defined NULL
  102.  *
  103.  */
  104.  
  105. ndptr hashtab[HASHSIZE];    /* hash table for macros etc.  */
  106. char buf[BUFSIZE];        /* push-back buffer           */
  107. char *bp = buf;         /* first available character   */
  108. char *endpbb = buf+BUFSIZE;    /* end of push-back buffer     */
  109. stae mstack[STACKMAX+1];     /* stack of m4 machine         */
  110. char strspace[STRSPMAX+1];    /* string space for evaluation */
  111. char *ep = strspace;        /* first free char in strspace */
  112. char *endest= strspace+STRSPMAX;/* end of string space           */
  113. int sp;             /* current m4  stack pointer   */
  114. int fp;             /* m4 call frame pointer       */
  115. File infile[MAXINP];        /* input file stack            */
  116. File outfile[MAXOUT];        /* diversion array             */
  117. FILE *active;            /* active output file pointer  */
  118. char *m4temp;            /* filename for diversions     */
  119. char *m4divnum;            /* pointer to divnum in m4temp */
  120. char *tempdir;            /* directory for temp files    */
  121. int ilevel = 0;         /* input file stack pointer    */
  122. int oindex = 0;         /* diversion index..           */
  123. char *null = "";                /* as it says.. just a null..  */
  124. char *m4wraps = "";             /* m4wrap string default..     */
  125. char lquote = LQUOTE;        /* left quote character  (`)   */
  126. char rquote = RQUOTE;        /* right quote character (')   */
  127. char scommt = SCOMMT;        /* start character for comment */
  128. char ecommt = ECOMMT;        /* end character for comment   */
  129. struct keyblk keywrds[] = {    /* m4 keywords to be installed */
  130.     "include",      INCLTYPE,
  131.     "sinclude",     SINCTYPE,
  132.     "define",       DEFITYPE,
  133.     "defn",         DEFNTYPE,
  134.     "divert",       DIVRTYPE,
  135.     "expr",         EXPRTYPE,
  136.     "eval",         EXPRTYPE,
  137.     "substr",       SUBSTYPE,
  138.     "ifelse",       IFELTYPE,
  139.     "ifdef",        IFDFTYPE,
  140.     "len",          LENGTYPE,
  141.     "incr",         INCRTYPE,
  142.     "decr",         DECRTYPE,
  143.     "dnl",          DNLNTYPE,
  144.     "changequote",  CHNQTYPE,
  145.     "changecom",    CHNCTYPE,
  146.     "index",        INDXTYPE,
  147. #ifdef EXTENDED
  148.     "paste",        PASTTYPE,
  149.     "spaste",       SPASTYPE,
  150. #endif
  151.     "popdef",       POPDTYPE,
  152.     "pushdef",      PUSDTYPE,
  153.     "dumpdef",      DUMPTYPE,
  154.     "shift",        SHIFTYPE,
  155.     "translit",     TRNLTYPE,
  156.     "undefine",     UNDFTYPE,
  157.     "undivert",     UNDVTYPE,
  158.     "divnum",       DIVNTYPE,
  159.     "maketemp",     MKTMTYPE,
  160.     "errprint",     ERRPTYPE,
  161.     "m4wrap",       M4WRTYPE,
  162.     "m4exit",       EXITTYPE,
  163.     "syscmd",       SYSCTYPE,
  164.     "sysval",       SYSVTYPE,
  165.     "arm",          MACRTYPE,
  166. };
  167.  
  168. #define MAXKEYS    (sizeof(keywrds)/sizeof(struct keyblk))
  169.  
  170. /* Internal routines */
  171.  
  172. static void  initkwds (void);
  173. static void  initm4 (void);
  174. static ndptr inspect (char *tp);
  175. static void  macro (void);
  176.  
  177. int main (int argc, char *argv[])
  178. {
  179.     register int c;
  180.     register int n;
  181.     int dirlen;
  182.     char *p;
  183.  
  184.     _kernel_osfile_block dummy;
  185.  
  186.     if (signal(SIGINT, SIG_IGN) != SIG_IGN)
  187.         signal(SIGINT, onintr);
  188.  
  189. #ifdef NONZEROPAGES
  190.     initm4();
  191. #endif
  192.  
  193.     initkwds();
  194.  
  195.     tempdir = getenv("Tmp$Root");
  196.  
  197.     if (tempdir == NULL)
  198.     {
  199.         /* Does "&.Tmp" exist? */
  200.         if (_kernel_osfile(5,"&.Tmp",&dummy) == 2)
  201.             tempdir = "&.Tmp";
  202.         else
  203.             tempdir = "";
  204.     }
  205.  
  206.     while ((c = getopt(argc, argv, "?D:o:T:U:")) != EOF)
  207.     {
  208.         switch(c)
  209.         {
  210.  
  211.         case 'D':               /* define something  */
  212.             for (p = optarg; *p; p++)
  213.                 if (*p == '=')
  214.                     break;
  215.             if (*p)
  216.                 *p++ = EOS;
  217.             dodefine(optarg, p);
  218.             break;
  219.  
  220.         case 'o':        /* specific output   */
  221.             if ( !freopen(optarg, "w", stdout) )
  222.             {
  223.                 fprintf(stderr, "M4: Cannot open %s\n", optarg);
  224.                 exit(1);
  225.             }
  226.             break;
  227.  
  228.         case 'T':        /* temp file dir     */
  229.             /* Is it a directory? */
  230.             if (_kernel_osfile(5,optarg,&dummy) != 2)
  231.                 error("M4: %s is not a directory",optarg);
  232.             tempdir = optarg;
  233.             break;
  234.         
  235.         case 'U':               /* undefine          */
  236.             remhash(optarg, TOP);
  237.             break;
  238.  
  239.         case '?':
  240.         default:
  241.             usage();
  242.         }
  243.     }
  244.  
  245.     /* Set up the diversion file name(s) */
  246.  
  247.     dirlen = strlen(tempdir);
  248.     
  249.     if (dirlen != 0)
  250.         ++dirlen;        /* add one for the dot         */
  251.  
  252.     m4temp = malloc(dirlen+DIVLEN+1);
  253.  
  254.     if (!m4temp)
  255.         error("M4: Heap full");
  256.  
  257.     if (dirlen)
  258.     {
  259.         strcpy(m4temp,tempdir);
  260.         m4temp[dirlen-1] = '.';
  261.     }
  262.  
  263.     strcpy(&m4temp[dirlen],DIVNAM);
  264.  
  265.     m4divnum = &m4temp[dirlen+DIVNUM];
  266.  
  267.     sp = -1;            /* stack pointer initialized   */
  268.     fp = 0;             /* frame pointer initialized   */
  269.     active = stdout;        /* default active output       */
  270.  
  271.     if ( optind >= argc )
  272.     {
  273.         infile[0].name = "-";
  274.         infile[0].fp   = stdin;
  275.         infile[0].ptr  = 0L;
  276.         macro();
  277.     }
  278.     else
  279.     {
  280.         for ( ; optind < argc; ++optind )
  281.         {
  282.             char *file = argv[optind];
  283.  
  284.             infile[0].name = file;
  285.             infile[0].ptr  = 0L;
  286.  
  287.             if (file[0] == '-' && file[1] == '\0')
  288.                 infile[0].fp = stdin;
  289.             else
  290.             {
  291.                 infile[0].fp = fopen(file,"r");
  292.  
  293.                 if ( !infile[0].fp )
  294.                     error("M4: Cannot open %s", file);
  295.             }
  296.  
  297.             /* Set the input file stack back to empty */
  298.             ilevel = 0;
  299.             macro();
  300.         }
  301.     }
  302.  
  303.     if (*m4wraps) {         /* anything for rundown ??     */
  304.         ilevel = 0;        /* in case m4wrap includes..   */
  305.         putback(EOF);        /* eof is a must !!           */
  306.         pbstr(m4wraps);     /* user-defined wrapup act     */
  307.         macro();        /* last will and testament     */
  308.     }
  309.     else                /* default wrap-up: undivert   */
  310.     {
  311.         for (n = 1; n < MAXOUT; n++)
  312.         {
  313.             if (outfile[n].ptr)
  314.                 getdiv(n);
  315.         }
  316.     }
  317.  
  318.     return 0;
  319. }
  320.  
  321. /*
  322.  * macro - the work horse..
  323.  *
  324.  */
  325. static void macro (void)
  326. {
  327.     char token[MAXTOK];
  328.     register char *s;
  329.     register int t, l;
  330.     register ndptr p;
  331.     register int  nlpar;
  332.  
  333.     forever
  334.     {
  335.         if ((t = gpbc()) == '_' || isalpha(t))
  336.         {
  337.             putback(t);
  338.             if ((p = inspect(s = token)) == nil)
  339.                 putstr(s);
  340.             else {
  341.         /*
  342.          * real thing.. First build a call frame:
  343.          *
  344.          */
  345.                 pushf(fp);    /* previous call frm */
  346.                 pushf(p->type); /* type of the call  */
  347.                 pushf(0);    /* parenthesis level */
  348.                 fp = sp;    /* new frame pointer */
  349.         /*
  350.          * now push the string arguments:
  351.          *
  352.          */
  353.                 pushs(p->defn);          /* defn string */
  354.                 pushs(p->name);          /* macro name  */
  355.                 pushs(ep);          /* start next..*/
  356.  
  357.                 putback(l = gpbc());
  358.                 if (l != LPAREN)  {   /* add bracks  */
  359.                     putback(RPAREN);
  360.                     putback(LPAREN);
  361.                 }
  362.             }
  363.         }
  364.  
  365.     /*
  366.      * End of file - close this file
  367.      * and go up one level in the
  368.      * list of included files.
  369.      */
  370.         else if (t == EOF) {
  371.  
  372.             if (sp > -1)
  373.                 error("M4: Unexpected end of input");
  374.  
  375.             /* Have we all the input? */
  376.             if (--ilevel < 0)
  377.                 break;
  378.  
  379.             (void) fclose(infile[ilevel+1].fp);
  380.  
  381.             /* Check for stdin... */
  382.             if (INNAME[0] == '-' && INNAME[1] == '\0')
  383.                 INFP = stdin;
  384.             else
  385.             {
  386.                 /* Reopen the previous file */
  387.                 if ( (INFP = fopen(INNAME,"r")) == NULL )
  388.                     error("M4: Cannot reopen %s", INNAME);
  389.  
  390.                 if ( fseek(INFP, INPTR, SEEK_SET) )
  391.                     error("M4: Cannot return to previous "
  392.                     "position in %s", INNAME);
  393.             }
  394.  
  395.             continue;
  396.         }
  397.     /*
  398.      * non-alpha single-char token seen..
  399.      * [the order of else if .. stmts is
  400.      * important.]
  401.      *
  402.      */
  403.         else if (t == lquote) {         /* strip quotes */
  404.             nlpar = 1;
  405.             do {
  406.                 if ((l = gpbc()) == rquote)
  407.                     nlpar--;
  408.                 else if (l == lquote)
  409.                     nlpar++;
  410.                 else if (l == EOF)
  411.                     error("M4: Missing right quote");
  412.                 if (nlpar > 0)
  413.                     putchr(l);
  414.             }
  415.             while (nlpar != 0);
  416.         }
  417.  
  418.         else if (sp < 0)
  419.         {
  420.             /* not in a macro at all */
  421.  
  422.             /* Check for comments */
  423.             if (t == scommt)
  424.             {
  425.  
  426.                 /* Skip the comment */
  427.                 while ((t = gpbc()) != ecommt)
  428.                     ;
  429.  
  430.                 continue;
  431.             }
  432.  
  433.             /* Output the character */
  434.             if (active != NULL)
  435.                 putc(t, active);
  436.         }
  437.  
  438.         else switch(t)
  439.         {
  440.  
  441.         case LPAREN:
  442.             if (PARLEV > 0)
  443.                 putchr(t);
  444.             while (isspace(l = gpbc()))
  445.                 ;        /* skip blank, tab, nl.. */
  446.             putback(l);
  447.             PARLEV++;
  448.             break;
  449.  
  450.         case RPAREN:
  451.             if (--PARLEV > 0)
  452.                 putchr(t);
  453.             else {            /* end of argument list */
  454.                 putchr(EOS);
  455.  
  456.                 if (sp == STACKMAX)
  457.                     error("M4: Internal stack overflow");
  458.  
  459.                 if (CALTYP == MACRTYPE)
  460.                     expand((char **)(mstack+fp+1), sp-fp);
  461.                 else
  462.                     eval((char **)(mstack+fp+1), sp-fp, CALTYP);
  463.  
  464.                 ep = PREVEP;    /* flush strspace */
  465.                 sp = PREVSP;    /* previous sp..  */
  466.                 fp = PREVFP;    /* rewind stack...*/
  467.             }
  468.             break;
  469.  
  470.         case COMMA:
  471.             if (PARLEV == 1)    {
  472.                 putchr(EOS);        /* new argument   */
  473.                 while (isspace(l = gpbc()))
  474.                     ;
  475.                 putback(l);
  476.                 pushs(ep);
  477.             }
  478.             break;
  479.         default:
  480.             putchr(t);            /* stack the char */
  481.             break;
  482.         }
  483.     }
  484. }
  485.  
  486.  
  487. /*
  488.  * build an input token..
  489.  * consider only those starting with _ or A-Za-z. This is a
  490.  * combo with lookup to speed things up.
  491.  */
  492. static ndptr inspect (char *tp) 
  493. {
  494.     register int h = 0;
  495.     register char c;
  496.     register char *name = tp;
  497.     register char *etp = tp+MAXTOK;
  498.     register ndptr p;
  499.  
  500.     while (tp < etp && (isalnum(c = gpbc()) || c == '_'))
  501.         h += (*tp++ = c);
  502.     putback(c);
  503.     if (tp == etp)
  504.         error("M4: Token too long");
  505.     *tp = EOS;
  506.     for (p = hashtab[h%HASHSIZE]; p != nil; p = p->nxtptr)
  507.         if (strcmp(name, p->name) == 0)
  508.             break;
  509.     return(p);
  510. }
  511.  
  512. #ifdef NONZEROPAGES
  513. /*
  514.  * initm4 - initialize various tables. Useful only if your system
  515.  * does not know anything about demand-zero pages.
  516.  *
  517.  */
  518. static void initm4 (void)
  519. {
  520.     register int i;
  521.  
  522.     for (i = 0; i < HASHSIZE; i++)
  523.         hashtab[i] = nil;
  524.  
  525.     for (i = 0; i < MAXOUT; i++)
  526.     {
  527.         outfile[i].name = "";
  528.         outfile[i].fp   = NULL;
  529.         outfile[i].ptr  = 0L;
  530.     }
  531. }
  532. #endif
  533.  
  534. /*
  535.  * initkwds - initialise m4 keywords as fast as possible. 
  536.  * This very similar to install, but without certain overheads,
  537.  * such as calling lookup. Malloc is not used for storing the 
  538.  * keyword strings, since we simply use the static  pointers
  539.  * within keywrds block. We also assume that there is enough memory 
  540.  * to at least install the keywords (i.e. malloc won't fail).
  541.  *
  542.  */
  543. static void initkwds (void)
  544. {
  545.     register int i;
  546.     register int h;
  547.     register ndptr p;
  548.  
  549.     for (i = 0; i < MAXKEYS; i++) {
  550.         h = hash(keywrds[i].knam);
  551.         p = (ndptr) malloc(sizeof(struct ndblock));
  552.         p->nxtptr = hashtab[h];
  553.         hashtab[h] = p;
  554.         p->name = keywrds[i].knam;
  555.         p->defn = null;
  556.         p->type = keywrds[i].ktyp | STATIC;
  557.     }
  558. }
  559.